package com.ingenic.glass.camera;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
import android.hardware.Camera;
import android.location.Location;
import android.media.CamcorderProfile;
import android.media.CameraProfile;
import android.media.MediaRecorder;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.os.Process;
import android.os.Build;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
import android.provider.Settings;
import android.provider.MediaStore;
import android.provider.MediaStore.Video;
import android.util.Log;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.GestureDetector;
import android.widget.GestureDetector.SimpleOnGestureListener;
import android.filterpacks.videosink.MediaRecorderStopException;

import java.lang.ref.WeakReference;
import java.util.Locale;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.ingenic.glass.camera.ui.RotateLayout;
import com.ingenic.glass.camera.util.StorageSpaceUtil;
import com.ingenic.glass.camera.util.Util;
// import com.ingenic.glass.camera.util.A2DPUtil;
import com.ingenic.glass.camera.gallery.GalleryPicker;
import com.ingenic.glass.camera.gallery.BitmapManager;
import android.os.BatteryManager;
import android.provider.Settings.SettingNotFoundException;
import com.ingenic.glass.voicerecognizer.api.VoiceRecognizer;
import com.ingenic.glass.voicerecognizer.api.VoiceRecognizerListener;

/**
 * The Camcorder activity.
 */
public class VideoActivity2 extends Activity
    implements /*CameraPreference.OnPreferenceChangedListener,*/
	       SurfaceHolder.Callback,
	       MediaRecorder.OnErrorListener, 
	       MediaRecorder.OnInfoListener2,
	       EffectsRecorder.EffectsListener,OnTouchListener,PictureCallback{    
    private static final String TAG = "VideoActivity2";

    private static final String EXTRA_AUDIO_MODE = "audio.mode";
    private static final String SET_AUDIO_MODE_ACTION = "action.set.audio.mode";
    private int mValidAudioMode = AudioManager.MODE_INVALID;
    private static final int START_PREVIEW = 1;
    private static final int UPDATE_RECORD_TIME = 4;
    private static final int MSG_TTS_END = 5;
    private static final int MSG_TTS_CANCEL = 7;

    private static final int SCREEN_DELAY = 2 * 60 * 1000;
    // Sort
    private static final String EFFECT_BG_FROM_GALLERY = "gallery";

    private static VideoActivity2 mInstance = null;

    private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();

    private ComboPreferences mPreferences;
    private FrameLayout mPreviewFrameLayout;
    private SurfaceHolder mSurfaceHolder = null;
    private int mSurfaceWidth;
    private int mSurfaceHeight;

    /*device w&h*/
    private int mWindowsWidth;
    private int mWindowsHeight;

    //store current picture
    private TextView mRecordingTimeView;
    private View mBgLearningMessageFrame;

    private boolean mIsVideoCaptureIntent;

    private long mStorageSpace;

    private MediaRecorder mMediaRecorder;
    private EffectsRecorder mEffectsRecorder;

    private int mEffectType = EffectsRecorder.EFFECT_NONE;
    private Object mEffectParameter = null;
    private String mEffectUriFromGallery = null;
    private String mPrefVideoEffectDefault;
    private boolean mResetEffect = true;
    public static final String RESET_EFFECT_EXTRA = "reset_effect";

    private boolean mCameraPictureTaking = false;
    private volatile static boolean mMediaRecorderRecording = false;
    private long mRecordingStartTime;
    private boolean mRecordingTimeCountsDown = false;
    private RotateLayout mRecordingTimeRect;
    // The video file that the hardware camera is about to record into
    // (or is recording into.)
    private String mVideoFilename = null;
    private ParcelFileDescriptor mVideoFileDescriptor;

    // Added by dybai_bj 20150702 Add subsection recode configure And Driving recorder mode.
    private static final int CAR_MODE_N = 0;
    private static final int CAR_MODE_Y = 1;

    private long mSubsectionTimed = 0; // Unit is minute.
    private int mCarMode = CAR_MODE_N;
    private Queue<File> mVideoFileQueue = null;

    private CamcorderProfile mProfile;

    // The video duration limit. 0 menas no limit.
    private int mMaxVideoDurationInMs;
    private boolean mCaptureTimeLapse = false;
    // Default 0. If it is larger than 0, the camcorder is in time lapse mode.
    private int mTimeBetweenTimeLapseFrameCaptureMs = 0;
    private View mTimeLapseLabel;

    private int mDesiredPreviewWidth;
    private int mDesiredPreviewHeight;

    // The display rotation in degrees. This is only valid when mPreviewing is
    // true.
    private int mDisplayRotation;
    private ContentResolver mContentResolver;
    private LocationManager mLocationManager;
    private MyHandler mHandler;
    private Parameters mParameters;

    // multiple cameras support
    private int mNumberOfCameras;
    private int mCameraId;
    private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
    private int mOrientationCompensation = 0;
    //gallery
    private GestureDetector mGestureDetector;
    private Thread mStartPreviewThread;
    private WakeLock mWakeLock;

    // check incall state and add tts
    private AudioManager mAudioManager ;
    private Object mLock = new Object();
    private boolean mNeedStartPreview = false;
    private String mTTS = null;
    private boolean mFinished = false;
    private boolean mIsReleaseResource = false;
    private boolean misCameraLiveIn = false;
    private Camera mCameraDevice;
    private VoiceRecognizer mVoiceRecognizer;

    private static final int ERROR_TYPE_NONE = 0;
    private static final int ERROR_TYPE_NOSPACE = 1;
    private static final int ERROR_TYPE_LOWPOWER = 2;
    private static final int ERROR_TYPE_RECODEFAILED = 3;
    private static final int ERROR_TYPE_CAMERA_DISCONNECT = 4;
    private static final int ERROR_TYPE_CAMERA_DISABLED = 5;
    private static final int ERROR_TYPE_CAMERA_BUSY = 6;
    private int mErrorType = ERROR_TYPE_NONE;

    private CameraAppImpl mApplication;
    private int mPicWaterMark = 0;
    private int mVideoWaterMark = 0;
    AlertDialog mAlertDialog;
    private MyBroadcastReceiver mReceiver;
    private MyVoiceRecognizerListener mVoiceRecognizerListener;
    // This Handler is used to post message back onto the main thread of the
    // application

    private static class MyHandler extends Handler {
	 WeakReference<Activity > mActivityReference;
	    
	 MyHandler(Activity activity) {
		 mActivityReference= new WeakReference<Activity>(activity);
	 }
	    
	    @Override
	    public void handleMessage(Message msg) {
		final VideoActivity2 videoActivity = (VideoActivity2)mActivityReference.get();
		if (videoActivity != null) {
		     if (CameraAppImpl.DEBUG) Log.d(TAG,"handleMessage in msg.what="+msg.what);
		     switch (msg.what) {
		     case UPDATE_RECORD_TIME: {
			     videoActivity.checkVideoFileSize();
			     videoActivity.updateRecordingTime();
			     break;
		     }
		     case START_PREVIEW:{	
			     /*get Reord intent from voice system*/
			     if (CameraAppImpl.DEBUG) Log.d(TAG,"-------start recording");
			     synchronized (videoActivity.mLock) {		    
				     if (videoActivity.mFinished)
					     return;
			     }
			     
			     Intent in = new Intent("cn.ingenic.glass.ACTION_MEDIA_VIDEO_START");
			     videoActivity.sendBroadcast(in);
			     
			     try {
				  videoActivity.startVideoRecording();
			     } catch (Exception e) {
				  e.printStackTrace();
				  Toast.makeText(videoActivity, e.getMessage(), Toast.LENGTH_LONG).show();
				  videoActivity.finish();
			     }
			     break;
		     }
		     case MSG_TTS_END:{
			     videoActivity.onTTSPlayEnd((String) msg.obj);
			     break;
		     }
		     case MSG_TTS_CANCEL:{
			     videoActivity.onTTSPlayCanceled((String) msg.obj);
			     break;
		     }
		     default:
			     if (CameraAppImpl.DEBUG) Log.d(TAG, "Unhandled message: " + msg.what);
			     break;
		     }
		}
	    }
    }


    private static class MyBroadcastReceiver extends BroadcastReceiver{
	 WeakReference<Activity > mActivityReference;
	    
	 MyBroadcastReceiver(Activity activity) {
		 mActivityReference= new WeakReference<Activity>(activity);
	 }

	 @Override
	 public void onReceive(Context context, Intent intent) {
	     final VideoActivity2 videoActivity = (VideoActivity2)mActivityReference.get();
	     String action = intent.getAction();
	     Uri uri = intent.getData();
	     if (videoActivity != null) {
		 if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
		     videoActivity.updateAndShowStorageHint();
		     videoActivity.stopVideoRecording();
		     videoActivity.finish();
		 } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
			 videoActivity.updateAndShowStorageHint();
		 } else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
			 // SD card unavailable
		 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)
			    && uri.toString().equals(CameraAppImpl.SCAN_EXTERNAL_URI)) {
			 videoActivity.updateAndShowStorageHint();
		 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)){
			 int currentBatteryVoltage = 
				 intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE
						    ,CameraAppImpl.LOWEST_BATTERY_VOLTAGE);
			 if (CameraAppImpl.DEBUG) 
				 Log.d(TAG,"battery has change :: currentBatteryVoltage= "
				       +currentBatteryVoltage);
			 if (currentBatteryVoltage <= CameraAppImpl.LOWEST_BATTERY_VOLTAGE){
				 videoActivity.mErrorType = ERROR_TYPE_LOWPOWER;
				 videoActivity.finish();
			 }
		 } else if (action.equals(SET_AUDIO_MODE_ACTION)) {
			 int mode = intent.getIntExtra(EXTRA_AUDIO_MODE, AudioManager.MODE_NORMAL);
			 Log.d(TAG,"set mode to " + mode + " isRecording:"+isRecording());
			 // We should do no-audio recording before audio mode being set to MODE_IN_CALL,
			 // and do with-audio recording after audio mode being set to other mode.
			 videoActivity.mValidAudioMode = mode;
			 if (CameraAppImpl.NO_SCREEN_DEVICE && isRecording()){
				 videoActivity.stopVideoRecording();
				 videoActivity.startVideoRecording();
			 }
		 }else if (action.equals(Intent.ACTION_SHUTDOWN)){
			 videoActivity.finish();
		 }	     
	     }
	 } 
    }

    private String createName(long dateTaken) {
        Date date = new Date(dateTaken);
        String strFormat = null;
        // Modifyed by dybai_bj 20150703 Add subsection recode configure And Driving recorder mode.
        if (mCaptureTimeLapse)
        	strFormat = getString(R.string.video_no_sound_file_name_format);
        if (strFormat == null) {
        	strFormat = getString(R.string.video_file_name_format);
        }
        SimpleDateFormat dateFormat = new SimpleDateFormat(strFormat, Locale.US);
        return dateFormat.format(date);
    }

    @Override
	public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
	if (CameraAppImpl.DEBUG) Log.d(TAG,"onCreate in");
	VideoActivity2.setMInstance(this);
	mHandler = new MyHandler(this);
	requestWindowFeature(Window.FEATURE_PROGRESS);
	mWindowsWidth = getWindowManager().getDefaultDisplay().getWidth();  
	mWindowsHeight = getWindowManager().getDefaultDisplay().getHeight();
	setContentView(R.layout.main);
	ViewGroup root  = (ViewGroup) findViewById(R.id.main);
	MyView view = (MyView) getLayoutInflater().inflate(R.layout.video_camera, null);
	view.setLayoutParams(new LayoutParams(mWindowsWidth, mWindowsHeight));
	root.addView(view);  
        view.setHandler(mHandler);
	mPreviewFrameLayout = (FrameLayout) view.findViewById(R.id.frame);
	RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mPreviewFrameLayout.getLayoutParams();
	lp.width=mWindowsWidth;
	lp.height=mWindowsWidth*3/4;
	mPreviewFrameLayout.setLayoutParams(lp);
        SurfaceView sv = (SurfaceView) view.findViewById(R.id.camera_preview);
        SurfaceHolder holder = sv.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mRecordingTimeView = (TextView) findViewById(R.id.recording_time);
        mRecordingTimeRect = (RotateLayout) findViewById(R.id.recording_time_rect);
        mTimeLapseLabel = findViewById(R.id.time_lapse_label);
        mBgLearningMessageFrame = findViewById(R.id.bg_replace_message_frame);
        showTimeLapseUI(mCaptureTimeLapse);
	mGestureDetector = new GestureDetector(this, new MySimpleGestureDetectorListener(this));
	root.setOnTouchListener(this);
	mLocationManager = new LocationManager(this, null);

	mApplication = (CameraAppImpl)getApplication();
	mApplication.acquireWakeLock();
	mVoiceRecognizerListener = new MyVoiceRecognizerListener(this);
	mVoiceRecognizer = new VoiceRecognizer(VoiceRecognizer.REC_TYPE_COMMAND, mVoiceRecognizerListener);
	mVoiceRecognizer.setAppName(getString(R.string.video_recorder_label));
	mVoiceRecognizer.register();
	mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
	mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
					AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
	readSubsectionConfig();
        mReceiver = new MyBroadcastReceiver(this);
	 // install an intent filter to receive SD card related events.
        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
        intentFilter.addAction(Intent.ACTION_MEDIA_EJECT);
        intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
        intentFilter.addDataScheme("file");
        registerReceiver(mReceiver, intentFilter);
	IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
	filter.addAction(SET_AUDIO_MODE_ACTION);
	filter.addAction(Intent.ACTION_SHUTDOWN);
	registerReceiver(mReceiver, filter);

	int currentBatteryVoltage  = 0;
	try{
		currentBatteryVoltage = Settings.System.getInt(getContentResolver(),
							       "batteryVoltage");
	}catch(SettingNotFoundException  e){
	    e.printStackTrace();
	}
	
	if (CameraAppImpl.DEBUG) Log.d(TAG,"currentBatteryVoltage = "+currentBatteryVoltage);
	if (currentBatteryVoltage <= CameraAppImpl.LOWEST_BATTERY_VOLTAGE){
		mErrorType = ERROR_TYPE_LOWPOWER;
		finish();
		return;
	}

	mStorageSpace = StorageSpaceUtil.getAvailableSpace();
	if (mStorageSpace <= 0 && mCarMode == CAR_MODE_N) {
		mErrorType = ERROR_TYPE_NOSPACE;
		finish();
		return;
	}
	mVoiceRecognizer.lockRecognizer();

	init();

	if (mErrorType != ERROR_TYPE_NONE) {
	    finish();
	    return;
	}
	if (resetEffect()) {
	    mBgLearningMessageFrame.setVisibility(View.GONE);
	}
	mIsReleaseResource = false;
	Settings.System.putString(this.getContentResolver(), "camera_state","record");
	if (mAudioManager.getMode() != AudioManager.MODE_IN_CALL) {
		synchronized (mLock) {
			if (CameraAppImpl.USE_TTS) {
				mTTS = getString(R.string.tts_video_record_start);
				mVoiceRecognizer.playTTS(mTTS);
			}
		}
	}
	mVideoFileQueue = new LinkedList<File>();
	sortVideoFileList();
    }

    @Override
	public void onStart() {
        super.onStart();
	if (CameraAppImpl.DEBUG) Log.d(TAG,"--onStart in");
    }

    /**
     * Add subsection recode configure And Driving recorder mode.
     * @author Added by dybai_bj 20150703
     */
	private void readSubsectionConfig() {
		SharedPreferences sharedPreferences = 
			getSharedPreferences("com.ingenic.glass.video_record_preferences", 
					     Activity.MODE_PRIVATE);
		// mCarMode = sharedPreferences.getInt("CarMode", CAR_MODE_N);
		String duration = Settings.System.getString(getContentResolver(),"video_duration");
		if (duration != null)
			mSubsectionTimed = Integer.parseInt(duration);
		mSubsectionTimed = 1;
		  //mSubsectionTimed = sharedPreferences.getInt("SubsectionTimed", 10); // Unit is minute,default 10min.
		if (CameraAppImpl.DEBUG) Log.d(TAG, "CarMode = " + mCarMode 
					       + ", SubsectionTimed = " + mSubsectionTimed);
	}

    /**
     * Add subsection recode configure And Driving recorder mode.
     * @author Added by dybai_bj 20150703
     */
	private void sortVideoFileList() {
	    File videoDirectory = new File(Storage.DIRECTORY_VIDEO);
	    if (!videoDirectory.exists()) {
		videoDirectory.mkdirs();
		try {
		Runtime.getRuntime().exec("chmod 777 " + videoDirectory.getAbsolutePath());
		} catch (Exception e) {
			e.printStackTrace();
		}
	    } else if (!videoDirectory.isDirectory()) {
		videoDirectory.delete();
		videoDirectory.mkdirs();
		try {
		Runtime.getRuntime().exec("chmod 777 " + videoDirectory.getAbsolutePath());
		} catch (Exception e) {
			e.printStackTrace();
		}
	    }

	      // Filter the car video files.
	    File [] files = videoDirectory.listFiles();
	    if (null == files)
		    return;
	      // Sort the car video file list, according to the ascending order.
	    Arrays.sort(files, new Comparator<File>() {
		    @Override
			public int compare(File f1, File f2) {
			long result = f1.lastModified() - f2.lastModified();
			if (result < 0) {
			    return -1;
			} else if (result > 0) {
			    return 1;
			}
			return 0;
		    }
		});
	    for (int i = 0; i < files.length; i++) {
		mVideoFileQueue.add(files[i]);
	    }
	}

    @Override
	public void onStop() {
        super.onStop();
	if (CameraAppImpl.DEBUG) Log.d(TAG,"--onStop in");
	if (!CameraAppImpl.NO_SCREEN_DEVICE)
		finish();
    }

        private void releaseResource() {
	    if (CameraAppImpl.DEBUG) 
		Log.d(TAG, "releaseResource() in, mIsReleaseResource=" + mIsReleaseResource);
                if (mIsReleaseResource) {
                        return ;
                }
                mIsReleaseResource = true;
                finishRecorderAndCloseCamera();
        	closeVideoFileDescriptor();

		deleteVideoFile();
		if (CameraAppImpl.NO_SCREEN_DEVICE) {
			// delay 5s to release WakeLock in order to 
			// prevent TTS can't play end when system into deep sleep.
			mApplication.releaseWakeLock(5000);
		} else {
			mApplication.releaseWakeLock();
		}

        	if (mReceiver != null) {
        	        unregisterReceiver(mReceiver);
        	        mReceiver = null;
        	}

		if (mVoiceRecognizer != null) {
		      // don't callBack onTTSPlayEnd and onTTSPlayCanceled when unRegister.
		    mVoiceRecognizer.unRegister();
		}
        	if (mStorageHint != null) {
        	        mStorageHint.cancel();
        	        mStorageHint = null;
        	}

        	mLocationManager.recordLocation(false);
        }

    private void init(){
        Util.initializeScreenBrightness(getWindow(), getContentResolver());
        mPreferences = new ComboPreferences(this);
        CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
        mCameraId = CameraSettings.readPreferredCameraId(mPreferences);
	if (CameraAppImpl.DEBUG) Log.d(TAG,"---mCameraId="+mCameraId);
        //Testing purpose. Launch a specific camera through the intent extras.
        int intentCameraId = Util.getCameraFacingIntentExtras(this);
        if (intentCameraId != -1) {
            mCameraId = intentCameraId;
        }

        mPreferences.setLocalId(this, mCameraId);
        CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
        mNumberOfCameras = CameraHolder.instance().getNumberOfCameras();
        mPrefVideoEffectDefault = getString(R.string.pref_video_effect_default);
        // Do not reset the effect if users are switching between back and front
        // cameras.
        mResetEffect = getIntent().getBooleanExtra(RESET_EFFECT_EXTRA, true);
        resetEffect();

        /*
         * To reduce startup time, we start the preview in another thread.
         * We make sure the preview is started at the end of onCreate.
         */
        mStartPreviewThread = new Thread(new Runnable() {
		public void run() {
		    try {
			if (CameraAppImpl.DEBUG) Log.d(TAG,"---opencamera mCameraId="+mCameraId);
			mCameraDevice = Util.openCamera(VideoActivity2.this, mCameraId);
			if (mCameraDevice == null) {
			    mErrorType = ERROR_TYPE_CAMERA_DISCONNECT;
				return;
			} 
			mCameraDevice.setErrorCallback(mErrorCallback);
			mErrorCallback.setHandler(mHandler);
		    } catch (CameraHardwareException e) {
			mErrorType = ERROR_TYPE_CAMERA_DISCONNECT;
		    } catch (CameraDisabledException e) {
			mErrorType = ERROR_TYPE_CAMERA_DISABLED;
		    } catch (AssertionError e){
			if (CameraAppImpl.DEBUG) Log.d(TAG,"--==catch asserion error");
			mErrorType = ERROR_TYPE_CAMERA_BUSY;
 		    } catch (Exception e){
			mErrorType = ERROR_TYPE_CAMERA_BUSY;
		    }
		}
	    });
	mStartPreviewThread.start();
        Util.enterLightsOutMode(getWindow());
        mContentResolver = getContentResolver();       
        mIsVideoCaptureIntent = isVideoCaptureIntent();
        // Make sure preview is started.
        try {
            mStartPreviewThread.join();
        } catch (InterruptedException ex) {
            // ignore
        }
    }

    private OnScreenHint mStorageHint;

    private void updateAndShowStorageHint() {
        mStorageSpace = StorageSpaceUtil.getAvailableSpace();
        showStorageHint();
    }

    private void showStorageHint() {
        String errorMessage = null;
        if (mStorageSpace == StorageSpaceUtil.UNAVAILABLE) {
            errorMessage = getString(R.string.no_storage);
        } else if (mStorageSpace == StorageSpaceUtil.PREPARING) {
            errorMessage = getString(R.string.preparing_sd);
        } else if (mStorageSpace == StorageSpaceUtil.UNKNOWN_SIZE) {
            errorMessage = getString(R.string.access_sd_fail);
        } else if (mStorageSpace <= 0) {
            errorMessage = getString(R.string.spaceIsLow_content);
        }

        if (errorMessage != null) {
            if (mStorageHint == null) {
                mStorageHint = OnScreenHint.makeText(this, errorMessage);
            } else {
                mStorageHint.setText(errorMessage);
            }
            mStorageHint.show();
        } else if (mStorageHint != null) {
            mStorageHint.cancel();
            mStorageHint = null;
        }
    }

    private void readVideoPreferences(boolean hasAudio) {
        // The preference stores values from ListPreference and is thus string type for all values.
        // We need to convert it to int manually.
        String defaultQuality = CameraSettings.
		getDefaultVideoQuality(mCameraId,getResources().getString(R.string.pref_video_quality_default));
        String videoQuality =  mPreferences.getString(CameraSettings.KEY_VIDEO_QUALITY, defaultQuality);
        int quality = Integer.valueOf(videoQuality);

        // Set video quality.
        Intent intent = getIntent();
        if (intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) {
            int extraVideoQuality =
		intent.getIntExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
            if (extraVideoQuality > 0) {
                quality = CamcorderProfile.QUALITY_HIGH;
            } else {  // 0 is mms.
                quality = CamcorderProfile.QUALITY_LOW;
            }
        }

        // Set video duration limit. The limit is read from the preference,
        // unless it is specified in the intent.
        if (intent.hasExtra(MediaStore.EXTRA_DURATION_LIMIT)) {
            int seconds = intent.getIntExtra(MediaStore.EXTRA_DURATION_LIMIT, 0);
            mMaxVideoDurationInMs = 1000 * seconds;
        } else {
            mMaxVideoDurationInMs = CameraSettings.DEFAULT_VIDEO_DURATION;
        }

	if (CameraAppImpl.NO_SCREEN_DEVICE) {
		quality = CamcorderProfile.QUALITY_HIGH;
		// if is cruise board set it to mSubsecttionTimed,add by hky@sctek.cn 20150721
		//mMaxVideoDurationInMs = (int)mSubsectionTimed*60*1000;
	}
        // Set effect
        mEffectType = CameraSettings.readEffectType(mPreferences);
        if (mEffectType != EffectsRecorder.EFFECT_NONE) {
            mEffectParameter = CameraSettings.readEffectParameter(mPreferences);
            // Set quality to 480p for effects, unless intent is overriding it
            if (!intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) {
                quality = CamcorderProfile.QUALITY_480P;
            }
        } else {
            mEffectParameter = null;
        }
	if (!hasAudio) {
		mTimeBetweenTimeLapseFrameCaptureMs = 40;
		mCaptureTimeLapse = true ;
	} else {
		mTimeBetweenTimeLapseFrameCaptureMs = 0;
		mCaptureTimeLapse = false;
	}
        // TODO: This should be checked instead directly +1000.
        if (mCaptureTimeLapse) quality += 1000;
        mProfile = CamcorderProfile.get(mCameraId, quality);
        getDesiredPreviewSize();
    }

    private void writeDefaultEffectToPrefs()  {
        ComboPreferences.Editor editor = mPreferences.edit();
        editor.putString(CameraSettings.KEY_VIDEO_EFFECT,
			 getString(R.string.pref_video_effect_default));
        editor.apply();
    }

    private void getDesiredPreviewSize() {
	if (mCameraDevice == null)
	    return;
        mParameters = mCameraDevice.getParameters();
	mDesiredPreviewWidth = mProfile.videoFrameWidth;
	mDesiredPreviewHeight = mProfile.videoFrameHeight;
	if (CameraAppImpl.DEBUG) 
	    Log.d(TAG, "mProfile------mDesiredPreviewWidth=" + mDesiredPreviewWidth 
		  +". mDesiredPreviewHeight=" + mDesiredPreviewHeight);
    }

    private void setPreviewDisplay(SurfaceHolder holder) {
        try {
            if (effectsActive()) {
                mEffectsRecorder.setPreviewDisplay(mSurfaceHolder,mSurfaceWidth,mSurfaceHeight);
            } else {
                mCameraDevice.setPreviewDisplay(holder);
            }
        } catch (Throwable ex) {
            closeCamera();
            throw new RuntimeException("setPreviewDisplay failed", ex);
        }
    }
    private void closeCamera() {
	if (CameraAppImpl.DEBUG) Log.d(TAG, "closeCamera");
        if (mCameraDevice == null) {
            return;
        }
        if (mEffectsRecorder != null) {
            mEffectsRecorder.release();
        }
        mCameraDevice.stopPreview();
        mEffectType = EffectsRecorder.EFFECT_NONE;
        CameraHolder.instance().release();
        mCameraDevice.setErrorCallback(null);
        mCameraDevice = null;
    }

    private void finishRecorderAndCloseCamera() {
        // This is similar to what mShutterButton.performClick() does,
        // but not quite the same.
        stopVideoRecording();
        closeCamera();
    }

    @Override
    protected void onDestroy() {
	super.onDestroy();
	if (CameraAppImpl.DEBUG) Log.d(TAG,"onDestroy in");	
	finish();

	if(mAlertDialog != null){
	      /*fixbug:exception when mobile click video recorder button*/
	    mAlertDialog.dismiss();
	}
	
	mVoiceRecognizer.unlockRecognizer();
	mAudioManager.abandonAudioFocus(mAudioFocusListener);
	Settings.System.putString(this.getContentResolver(), "camera_state","");
	VideoActivity2.setMInstance(null);
    }

    @Override
    protected void onPause() {
	if (CameraAppImpl.DEBUG) Log.d(TAG,"onPause in");
	super.onPause();
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // Make sure we have a surface in the holder before proceeding.
        if (holder.getSurface() == null) {
	    	Log.w(TAG, "holder.getSurface() == null");
            return;
        }

	if (CameraAppImpl.DEBUG)  Log.d(TAG, "surfaceChanged. w=" + w + ". h=" + h);
        mSurfaceHolder = holder;
        mSurfaceWidth = w;
        mSurfaceHeight = h;
        SurfaceView preview = (SurfaceView) findViewById(R.id.camera_preview);

        if (mCameraDevice == null) return;

        if ((Util.getDisplayRotation(this) == mDisplayRotation)
	    && holder.isCreating()) {
            setPreviewDisplay(holder);
        } else {
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        mSurfaceHolder = null;
    }

    private boolean isVideoCaptureIntent() {
        String action = getIntent().getAction();
        return (MediaStore.ACTION_VIDEO_CAPTURE.equals(action));
    }

    // Prepares media recorder.
    private void initializeRecorder() {
        // If the mCameraDevice is null, then this activity is going to finish
        if (mCameraDevice == null) return;

        if (mSurfaceHolder == null) {
            	Log.w(TAG, "Surface holder is null. Wait for surface changed.");
            return;
        }

        Intent intent = getIntent();
        Bundle myExtras = intent.getExtras();

        long requestedSizeLimit = 0;
        closeVideoFileDescriptor();
        if (mIsVideoCaptureIntent && myExtras != null) {
            Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
            if (saveUri != null) {
                try {
                    mVideoFileDescriptor =
			mContentResolver.openFileDescriptor(saveUri, "rw");
                } catch (java.io.FileNotFoundException ex) {
                    // invalid uri
                    Log.e(TAG, ex.toString());
                }
            }
            requestedSizeLimit = myExtras.getLong(MediaStore.EXTRA_SIZE_LIMIT);
        }
        mMediaRecorder = new MediaRecorder();

        // Unlock the camera object before passing it to media recorder.
        mCameraDevice.unlock();
        mMediaRecorder.setCamera(mCameraDevice);
        if (!mCaptureTimeLapse) {
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        }

        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
	setProfile();
        mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);
        if (mCaptureTimeLapse) {
            mMediaRecorder.setCaptureRate((1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs));
        }

        Location loc = mLocationManager.getCurrentLocation();
        if (loc != null) {
            mMediaRecorder.setLocation((float) loc.getLatitude(),
				       (float) loc.getLongitude());
        }


        // Set output file.
        // Try Uri in the intent first. If it doesn't exist, use our own
        // instead.
        if (mVideoFileDescriptor != null) {
            mMediaRecorder.setOutputFile(mVideoFileDescriptor.getFileDescriptor());
        } else {
            generateVideoFilename(mProfile.fileFormat);
            createAndChangVideoFileMode();
            mMediaRecorder.setOutputFile(mVideoFilename);
        }

        mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());

        // Set maximum file size.
        long maxFileSize = mStorageSpace;
        if (requestedSizeLimit > 0 && requestedSizeLimit < maxFileSize) {
            maxFileSize = requestedSizeLimit;
        }

	if (CameraAppImpl.DEBUG) Log.d(TAG, "-------------maxFileSize:" + maxFileSize);
	checkVideoFileSize();
	if(mMediaRecorder == null)
		return;

        int rotation = 0;
        if (mOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
            CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
            if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
                rotation = (info.orientation - mOrientation + 360) % 360;
            } else {  // back-facing camera
                rotation = (info.orientation + mOrientation) % 360;
            }
        }
        mMediaRecorder.setOrientationHint(rotation);

        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            Log.e(TAG, "prepare failed for " + mVideoFilename, e);
            releaseMediaRecorder();
            throw new RuntimeException(e);
        }

        mMediaRecorder.setOnErrorListener(this);
        mMediaRecorder.setOnInfoListener2(this);
    }
    private void initializeEffectsRecording() {
	if (CameraAppImpl.DEBUG) Log.d(TAG, "initializeEffectsRecording");

        Intent intent = getIntent();
        Bundle myExtras = intent.getExtras();

        long requestedSizeLimit = 0;
        closeVideoFileDescriptor();
        if (mIsVideoCaptureIntent && myExtras != null) {
            Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
            if (saveUri != null) {
                try {
                    mVideoFileDescriptor =
			mContentResolver.openFileDescriptor(saveUri, "rw");
                } catch (java.io.FileNotFoundException ex) {
                    // invalid uri
                    Log.e(TAG, ex.toString());
                }
            }
            requestedSizeLimit = myExtras.getLong(MediaStore.EXTRA_SIZE_LIMIT);
        }

        mEffectsRecorder.setProfile(mProfile);
        // important to set the capture rate to zero if not timelapsed, since the
        // effectsrecorder object does not get created again for each recording
        // session
        if (mCaptureTimeLapse) {
            mEffectsRecorder.setCaptureRate((1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs));
        } else {
            mEffectsRecorder.setCaptureRate(0);
        }

        // Set output file
        if (mVideoFileDescriptor != null) {
            mEffectsRecorder.setOutputFile(mVideoFileDescriptor.getFileDescriptor());
        } else {
            generateVideoFilename(mProfile.fileFormat);
	    createAndChangVideoFileMode();
	    mEffectsRecorder.setOutputFile(mVideoFilename);
        }

        // Set maximum file size.
        long maxFileSize = mStorageSpace;
        if (requestedSizeLimit > 0 && requestedSizeLimit < maxFileSize) {
            maxFileSize = requestedSizeLimit;
        }
        mEffectsRecorder.setMaxFileSize(maxFileSize);
        mEffectsRecorder.setMaxDuration(mMaxVideoDurationInMs);
    }
    
    private void createAndChangVideoFileMode() {
    	try {
		File videoFile = new File(mVideoFilename);
		if(!videoFile.exists()) {
			videoFile.createNewFile();
			Runtime.getRuntime().exec("chmod 777 " + videoFile.getAbsolutePath());
		}	
	} catch (Exception e) {
		e.printStackTrace();
	}
    }

    private void releaseMediaRecorder() {
        if (mMediaRecorder != null) {
            mMediaRecorder.reset();
            mMediaRecorder.release();
            mMediaRecorder = null;
        }
    }

    private void releaseEffectsRecorder() {
        if (mEffectsRecorder != null) {
            mEffectsRecorder.release();
            mEffectsRecorder = null;
        }
    }

    private void generateVideoFilename(int outputFileFormat) {
        String title = createName(System.currentTimeMillis());
        File dir = new File(Storage.DIRECTORY_VIDEO);

        if(!dir.exists())
        	dir.mkdirs();

	mVideoFilename = Storage.DIRECTORY_VIDEO + '/' + title + ".tmp";
	if (CameraAppImpl.DEBUG) Log.d(TAG, "New video filename: " + mVideoFilename);
    }

    private void addVideoToMediaStore(String fileName) {
        if (mVideoFileDescriptor == null) {
            Uri videoTable = Uri.parse("content://media/external/video/media");
	    ContentValues currentVideoValues = new ContentValues(7);
	    // currentVideoValues.put(Video.Media.TITLE, title);
	    // currentVideoValues.put(Video.Media.DISPLAY_NAME, filename);
	    currentVideoValues.put(Video.Media.DATE_TAKEN, System.currentTimeMillis());
	    currentVideoValues.put(Video.Media.MIME_TYPE, convertOutputFormatToMimeType(mProfile.fileFormat));
	    currentVideoValues.put(Video.Media.DATA, fileName);
	    currentVideoValues.put(Video.Media.RESOLUTION,
				   Integer.toString(mProfile.videoFrameWidth) + "x" +
				   Integer.toString(mProfile.videoFrameHeight));

            currentVideoValues.put(Video.Media.SIZE,
				    new File(fileName).length());
	      //mCurrentVideoValues.put(Video.Media.DURATION, duration);

            try {
                Uri currentVideoUri = mContentResolver.insert(videoTable,
							   currentVideoValues);
                sendBroadcast(new Intent(android.hardware.Camera.ACTION_NEW_VIDEO,
					 currentVideoUri));
                BitmapManager.instance().getThumbnail(getContentResolver()
						      , ContentUris.parseId(currentVideoUri)
						      , Video.Thumbnails.MINI_KIND, null, true);
            } catch (Exception e) {
                // We failed to insert into the database. This can happen if
                // the SD card is unmounted.
		Log.e(TAG, "insert db failed! ");
            }
        }
    }

    private void deleteVideoFile() {
	if (CameraAppImpl.DEBUG) Log.d(TAG, "Deleting video ");
	if(mVideoFilename != null){
	    File f = new File(mVideoFilename);
	    if (f.exists() && !f.delete()) {
            	Log.w(TAG, "Could not delete " + mVideoFilename);
	    }
	    mVideoFilename = null;
	}
    }

    private void updateVideoFileName() {
        File f = new File(mVideoFilename);
        if (f.exists()) {
	    String fileName = mVideoFilename.replace(".tmp",convertOutputFormatToFileExt(mProfile.fileFormat));
	    File newFile = new File(fileName);
	    if (!f.renameTo(newFile)) {
		Log.e(TAG, "renameTo " + fileName +"failed!");
	    }
        }
    }

    // from MediaRecorder.OnErrorListener
    public void onError(MediaRecorder mr, int what, int extra) {
	Log.e(TAG, "MediaRecorder error. what=" + what + ". extra=" + extra);
        if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) {
            // We may have run out of space on the sdcard.
            stopVideoRecording();
            updateAndShowStorageHint();
        } else {
		Toast.makeText(VideoActivity2.this, "record failed", Toast.LENGTH_SHORT).show();
		mErrorType = ERROR_TYPE_RECODEFAILED;
		finish();
	}
    }

    // from MediaRecorder.OnInfoListener
    public void onInfo(MediaRecorder mr, int what, int extra,String str) {
        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
	    if (mMediaRecorderRecording) {
		    try {
			    stopVideoRecording();
			    // if (CameraAppImpl.NO_SCREEN_DEVICE)
			    // 	    startVideoRecording();
		    } catch (Exception e) {
			    e.printStackTrace();
			    Toast.makeText(VideoActivity2.this, e.getMessage(), Toast.LENGTH_LONG).show();
			    VideoActivity2.this.finish();
		    }
	    }
        } else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
	    if (mMediaRecorderRecording) {
            	try {
    		    	stopVideoRecording();
			startVideoRecording();
    	    	} catch (Exception e) {
    	    		e.printStackTrace();
    	    		Toast.makeText(VideoActivity2.this, e.getMessage(), Toast.LENGTH_LONG).show();
    	    	}
            }
            // Show the toast.
            Toast.makeText(this, R.string.video_reach_size_limit,
			   Toast.LENGTH_LONG).show();
            finish();
        } else if (what == MediaRecorder.MEDIA_RECORDER_FILE_NAME) {

		// if (CameraAppImpl.IS_CRUISE_BOARD)
		//     creatAndSaveVideoThumbnail(str);
		// else{
		    addVideoToMediaStore(str);
		// }

		// if (!mVideoFileQueue.offer(new File(str)))
		//     Log.d(TAG, "WARNING: Car video file added to queue failure!");
        }
    }

    public static boolean isRecording() {
        return mMediaRecorderRecording;
    }

    private void startVideoRecording() {
	if (CameraAppImpl.DEBUG) Log.d(TAG, "startVideoRecording");
	if (mCameraDevice == null) return;
	if (mMediaRecorderRecording || mFinished)
	    return;
	readSubsectionConfig();
	if(mValidAudioMode == AudioManager.MODE_INVALID)
		readVideoPreferences(mAudioManager.getMode() != AudioManager.MODE_IN_CALL);
	else
		readVideoPreferences(mValidAudioMode != AudioManager.MODE_IN_CALL);
	
	mValidAudioMode = AudioManager.MODE_INVALID;
	setCameraParameters();
        if(CameraAppImpl.DEBUG) Log.d(TAG, "startVideoRecording mCaptureTimeLapse:"+mCaptureTimeLapse);
        updateAndShowStorageHint();
        if (mStorageSpace <= 0) {
	    	Log.w(TAG, "Storage issue, ignore the start request");
            return;
        }

	synchronized (mLock) {
	    if (mTTS != null) {
		Log.w(TAG, "Need to wait tts play end.");
		mNeedStartPreview = true;
		return;
	    }
	    mNeedStartPreview = false;
	}

        if (effectsActive()) {
            initializeEffectsRecording();
            if (mEffectsRecorder == null) {
                Log.e(TAG, "Fail to initialize effect recorder");
                return;
            }
        } else {
            initializeRecorder();
            if (mMediaRecorder == null) {
                Log.e(TAG, "Fail to initialize media recorder");
		finish();
                return;
            }
        }

        if (effectsActive()) {
            try {
                mEffectsRecorder.startRecording();
            } catch (RuntimeException e) {
                Log.e(TAG, "Could not start effects recorder. ", e);
                releaseEffectsRecorder();
                return;
            }
        } else {
            try {
		if(mSubsectionTimed > 0){
		    deleteVideoFile();
		    String path = Storage.DIRECTORY_VIDEO;
		    path = path.replace("/storage/emulated/0/","/data/media/0/");
		    if (mCaptureTimeLapse){
			path = path + "/VID_noSound_";
		    }else
			path = path + "/VID_";

		    mMediaRecorder.setRecorderTimeIntervals((int)mSubsectionTimed*60,path);
		}
                mMediaRecorder.start(); // Recording is now started
            } catch (RuntimeException e) {
                Log.e(TAG, "Could not start media recorder. ", e);
                // If start fails, frameworks will not lock the camera for us.
                mCameraDevice.lock();
		Toast.makeText(VideoActivity2.this, "record failed", Toast.LENGTH_SHORT).show();
		mErrorType = ERROR_TYPE_RECODEFAILED;
		finish();
                return;
            }
        }
        mMediaRecorderRecording = true;
        mRecordingStartTime = SystemClock.uptimeMillis();
	showRecordingUI(true);
	updateRecordingTime();

    }
    
    private void showRecordingUI(final boolean recording) {
	if (CameraAppImpl.DEBUG) Log.d(TAG,"showRecordingUI in recording="+recording);
	runOnUiThread(new Runnable(){
		@Override
		public void run() {
			if (recording) {
				mRecordingTimeView.setText("");

				if(mWindowsHeight >=480)
					mRecordingTimeView.setTextSize(50);
				else
					mRecordingTimeView.setTextSize(30);

				mRecordingTimeView.setVisibility(View.VISIBLE);
			} else {
				mRecordingTimeView.setVisibility(View.GONE);
			}
		}
	    });
    }

    private void stopVideoRecording() {
	if (CameraAppImpl.DEBUG) Log.d(TAG, "stopVideoRecording");

        if (mMediaRecorderRecording) {
            try {
                if (effectsActive()) {
                    // This is asynchronous, so we can't add to media store now because thumbnail
                    // may not be ready. In such case addVideoToMediaStore is called later
                    // through a callback from the MediaEncoderFilter to EffectsRecorder,
                    // and then to the VideoCamera.
                    mEffectsRecorder.stopRecording();
                } else {
                    mMediaRecorder.setOnErrorListener(null);
                    mMediaRecorder.setOnInfoListener2(null);
                    mMediaRecorder.stop();
                }
            } catch (RuntimeException e) {
                Log.e(TAG, "stop fail",  e);
		deleteVideoFile();
            }
            mMediaRecorderRecording = false;
            showRecordingUI(false);

	    if (mErrorType != ERROR_TYPE_RECODEFAILED && mVideoFilename != null) {
		if(mSubsectionTimed > 0){ 
		    updateVideoFileName();
		}

		if (CameraAppImpl.IS_CRUISE_BOARD)
		    creatAndSaveVideoThumbnail(mVideoFilename);
		else{
		    addVideoToMediaStore(mVideoFilename);
		}

		if (!mVideoFileQueue.offer(new File(mVideoFilename)))
		    Log.d(TAG, "WARNING: Car video file added to queue failure!");
	    }
	 
        }

	mNeedStartPreview = false;

        // always release media recorder
        if (!effectsActive()) {
            releaseMediaRecorder();
        }
    }
    private static String millisecondToTimeString(long milliSeconds, boolean displayCentiSeconds) {
        long seconds = milliSeconds / 1000; // round down to compute seconds
        long minutes = seconds / 60;
        long hours = minutes / 60;
        long remainderMinutes = minutes - (hours * 60);
        long remainderSeconds = seconds - (minutes * 60);

        StringBuilder timeStringBuilder = new StringBuilder();

        // Hours
        if (hours > 0) {
            if (hours < 10) {
                timeStringBuilder.append('0');
            }
            timeStringBuilder.append(hours);

            timeStringBuilder.append(':');
        }

        // Minutes
        if (remainderMinutes < 10) {
            timeStringBuilder.append('0');
        }
        timeStringBuilder.append(remainderMinutes);
        timeStringBuilder.append(':');

        // Seconds
        if (remainderSeconds < 10) {
            timeStringBuilder.append('0');
        }
        timeStringBuilder.append(remainderSeconds);

        // Centi seconds
        if (displayCentiSeconds) {
            timeStringBuilder.append('.');
            long remainderCentiSeconds = (milliSeconds - seconds * 1000) / 10;
            if (remainderCentiSeconds < 10) {
                timeStringBuilder.append('0');
            }
            timeStringBuilder.append(remainderCentiSeconds);
        }

        return timeStringBuilder.toString();
    }

    private long getTimeLapseVideoLength(long deltaMs) {
        // For better approximation calculate fractional number of frames captured.
        // This will update the video time at a higher resolution.
        double numberOfFrames = (double) deltaMs / mTimeBetweenTimeLapseFrameCaptureMs;
        return (long) (numberOfFrames / mProfile.videoFrameRate * 1000);
    }

    private void updateRecordingTime() {
        if (!mMediaRecorderRecording) {
            return;
        }

	runOnUiThread(new Runnable(){
	    @Override
	    public void run() {
		long now = SystemClock.uptimeMillis();
		long delta = now - mRecordingStartTime;
		    
		// Starting a minute before reaching the max duration
		// limit, we'll countdown the remaining time instead.
		boolean countdownRemainingTime = (mMaxVideoDurationInMs != 0
						  && delta >= mMaxVideoDurationInMs - 60000);

		long deltaAdjusted = delta;
		if (countdownRemainingTime) {
		    deltaAdjusted = Math.max(0, mMaxVideoDurationInMs - deltaAdjusted) + 999;
		}
		String text;

		long targetNextUpdateDelay;
		if (!mCaptureTimeLapse) {
		    text = millisecondToTimeString(deltaAdjusted, false);
		    targetNextUpdateDelay = 1000;
		} else {
		    // The length of time lapse video is different from the length
		    // of the actual wall clock time elapsed. Display the video length
		    // only in format hh:mm:ss.dd, where dd are the centi seconds.
		    text = millisecondToTimeString(getTimeLapseVideoLength(delta), true);
		    targetNextUpdateDelay = mTimeBetweenTimeLapseFrameCaptureMs;
		}

		mRecordingTimeView.setText(text);

		if (mRecordingTimeCountsDown != countdownRemainingTime) {
		    // Avoid setting the color on every update, do it only
		    // when it needs changing.
		    mRecordingTimeCountsDown = countdownRemainingTime;

		    int color = getResources().getColor(countdownRemainingTime
						? R.color.recording_time_remaining_text
						: R.color.recording_time_elapsed_text); 

		    mRecordingTimeView.setTextColor(color);
		}
			
		long actualNextUpdateDelay = targetNextUpdateDelay - (delta % targetNextUpdateDelay);
		mHandler.sendEmptyMessageDelayed(
					 UPDATE_RECORD_TIME, actualNextUpdateDelay);
	    }
	});
    }

    private static boolean isSupported(String value, List<String> supported) {
        return supported == null ? false : supported.indexOf(value) >= 0;
    }
    @Override
	public void onEffectsUpdate(int effectId, int effectMsg) {
	if (CameraAppImpl.DEBUG) Log.d(TAG, "onEffectsUpdate in");
        if (effectMsg == EffectsRecorder.EFFECT_MSG_EFFECTS_STOPPED) {
            // Effects have shut down. Hide learning message if any,
            // and restart regular preview.
            mBgLearningMessageFrame.setVisibility(View.GONE);
            checkQualityAndStartPreview();
        } else if (effectMsg == EffectsRecorder.EFFECT_MSG_RECORDING_DONE) {
            addVideoToMediaStore(mVideoFilename);
        } else if (effectId == EffectsRecorder.EFFECT_BACKDROPPER) {
            switch (effectMsg) {
	    case EffectsRecorder.EFFECT_MSG_STARTED_LEARNING:
		mBgLearningMessageFrame.setVisibility(View.VISIBLE);
		break;
	    case EffectsRecorder.EFFECT_MSG_DONE_LEARNING:
	    case EffectsRecorder.EFFECT_MSG_SWITCHING_EFFECT:
		mBgLearningMessageFrame.setVisibility(View.GONE);
		break;
            }
        }
    }

    @Override
	public synchronized void onEffectsError(Exception exception, String fileName) {
	    // TODO: Eventually we may want to show the user an error dialog, and then restart the
	    // camera and encoder gracefully. For now, we just delete the file and bail out.
	    if (fileName != null && new File(fileName).exists()) {
		deleteVideoFile();
	    }
	    if (exception instanceof MediaRecorderStopException) {
		Log.w(TAG, "Problem recoding video file. Removing incomplete file.");
		return;
	    }
	    throw new RuntimeException("Error during recording!", exception);
	}

    @Override
	public void onConfigurationChanged(Configuration config) {
        super.onConfigurationChanged(config);
	if (CameraAppImpl.DEBUG) Log.d(TAG, "onConfigurationChanged in");
    }

    private boolean effectsActive() {
        return (mEffectType != EffectsRecorder.EFFECT_NONE);
    }

    private void checkQualityAndStartPreview() {
	readVideoPreferences(mAudioManager.getMode() != AudioManager.MODE_IN_CALL);
        showTimeLapseUI(mCaptureTimeLapse);
        Size size = mParameters.getPreviewSize();
        if (size.width != mDesiredPreviewWidth
	    || size.height != mDesiredPreviewHeight) {
        }
    }

    private void showTimeLapseUI(boolean enable) {
        if (mTimeLapseLabel != null) {
            mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE);
        }
    }
    private boolean resetEffect() {
        if (mResetEffect) {
            String value = mPreferences.getString(CameraSettings.KEY_VIDEO_EFFECT,
						  mPrefVideoEffectDefault);
            if (!mPrefVideoEffectDefault.equals(value)) {
                writeDefaultEffectToPrefs();
                return true;
            }
        }
        mResetEffect = true;
        return false;
    }

    private String convertOutputFormatToMimeType(int outputFileFormat) {
	if (CameraAppImpl.IS_CRUISE_BOARD)
	    return "video/mp4";
        if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) {
            return "video/mp4";
        }
	return "video/3gpp";
    }

    private String convertOutputFormatToFileExt(int outputFileFormat) {
	if (CameraAppImpl.IS_CRUISE_BOARD)
	    return ".mp4";
        if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) {
            return ".mp4";
        }
	return ".3gp";
    }

    private void closeVideoFileDescriptor() {
        if (mVideoFileDescriptor != null) {
            try {
                mVideoFileDescriptor.close();
            } catch (IOException e) {
                Log.e(TAG, "Fail to close fd", e);
            }
            mVideoFileDescriptor = null;
        }
    }

    @Override
    public void finish() {
	Log.i(TAG,"finish in");
	synchronized (mLock) {	
	    if (mFinished){
		super.finish();
		return;
	    }
	    mFinished = true;
	    
	}
	int errorTypeID = getErrorTypeID();
	    
	if (CameraAppImpl.USE_TTS && mAudioManager.getMode() != AudioManager.MODE_IN_CALL) {
	      /*when a call is active. audio hw samplerate is 8k.
	       * so can not play audio for 16k*/
	    if(mErrorType != ERROR_TYPE_CAMERA_BUSY)
	    mVoiceRecognizer.playTTS(getString(errorTypeID));
	}

	Intent in = new Intent("cn.ingenic.glass.ACTION_MEDIA_VIDEO_FINISH");
	sendBroadcast(in);

	releaseResource();

	if(CameraAppImpl.NO_SCREEN_DEVICE == false && mErrorType != ERROR_TYPE_NONE && mErrorType != ERROR_TYPE_CAMERA_BUSY){
	      /*for glass with screen : need show a dialog for error type*/
	    showAlertDialog(errorTypeID);
	    return;
	}

	if (CameraAppImpl.NO_SCREEN_DEVICE == false && 
	    mErrorType == ERROR_TYPE_NONE && 
	    misCameraLiveIn == false) {
	      /*has screen & no err*/
	    Intent intent = new Intent(VideoActivity2.this, GalleryPicker.class);
	    intent.putExtra("mode",CameraAppImpl.VIDEO);
	    startActivity(intent);
	}
	super.finish();
    }

    private int getErrorTypeID(){
	int resId = 0;
	switch(mErrorType) {
	case ERROR_TYPE_NOSPACE:
	    resId = R.string.tts_video_record_no_storage;
	    break;
	case ERROR_TYPE_LOWPOWER:
	    resId = R.string.tts_video_record_lowpower;
	    break;
	case ERROR_TYPE_RECODEFAILED:
	    resId = R.string.tts_video_record_error;
	    break;
	case ERROR_TYPE_CAMERA_DISCONNECT:
	    resId = R.string.camera_error_title;
	    break;
	case ERROR_TYPE_CAMERA_DISABLED:
	    resId = R.string.camera_error_title;
	    break;
	case ERROR_TYPE_CAMERA_BUSY:
	    resId = R.string.camera_error_busy;
	    break;
	default:
	    resId = R.string.tts_video_record_stop;
	    break;
	}
	return resId;
    }
    private void showAlertDialog(int resId) {
	AlertDialog.Builder builder = new AlertDialog.Builder(this);
	builder.setMessage(resId);
	builder.setCancelable(false);
	builder.setPositiveButton(R.string.positive, new DialogInterface.OnClickListener() {
		@Override
		    public void onClick(DialogInterface dialog, int arg1) {
		    dialog.dismiss();
		    mAlertDialog = null;
		    finish();			
		}
	    });

	mAlertDialog = builder.create();
	mAlertDialog.show();

    }

    private void setCameraParameters() {
	if (mCameraDevice == null)
		return;
	try {
		mPicWaterMark  = Settings.System.getInt(getContentResolver(), "pic_watermark");
		mVideoWaterMark  = Settings.System.getInt(getContentResolver(), "video_watermark");
	} catch(SettingNotFoundException e) {
	    Log.w(TAG,"not found pic_watermark & video_watermark in settings db");
	}
	mParameters.set("time_water_mark", CameraAppImpl.getWaterMarkMode(mPicWaterMark, mVideoWaterMark));
	if (CameraAppImpl.NO_SCREEN_DEVICE) {
		mParameters.set("preview_mode", CameraAppImpl.NO_SCREEN);
	} else {
		mParameters.set("preview_mode", CameraAppImpl.LOW_POWER_DISPLAY_VIDEO);
	}
	mParameters.setPreviewSize(mDesiredPreviewWidth,mDesiredPreviewHeight);
	mCameraDevice.setParameters(mParameters);	
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
	return mGestureDetector.onTouchEvent(event);	
    }

    private static class MySimpleGestureDetectorListener extends GestureDetector.SimpleOnGestureListener{
	WeakReference<Activity > mActivityReference;

	MySimpleGestureDetectorListener(Activity activity) {
		mActivityReference= new WeakReference<Activity>(activity);
	}

	@Override
	public boolean onSlideDown(boolean fromPhone){
	    final VideoActivity2 videoActivity = (VideoActivity2)mActivityReference.get();
	    if (videoActivity != null) {
		    videoActivity.finish();
	    }
	    return true;
	}
	@Override
	public boolean onTap(boolean fromPhone) {
	    final VideoActivity2 videoActivity = (VideoActivity2)mActivityReference.get();
	    if (videoActivity != null) {
		 if(videoActivity.mMediaRecorderRecording)
			 videoActivity.finish();
		 else
			 videoActivity.startVideoRecording();
	    } 
	    return true;
	}
    }

	public synchronized static void setMInstance(VideoActivity2 mInstance) {
		VideoActivity2.mInstance = mInstance;
	}

	public synchronized static VideoActivity2 getMInstance() {
		return mInstance;
	}

    public void setIsCameraLiveIn(boolean isCameraLiveIn) {
        this.misCameraLiveIn = isCameraLiveIn;
    }

    
    private static class MyVoiceRecognizerListener extends VoiceRecognizerListener{
	WeakReference<Activity > mActivityReference;
	    
	MyVoiceRecognizerListener(Activity activity) {
		mActivityReference= new WeakReference<Activity>(activity);
	}

	@Override
	public void onTTSPlayEnd(String tts) throws RemoteException {
		final VideoActivity2 videoActivity = (VideoActivity2)mActivityReference.get();
		if (videoActivity != null) {
		     videoActivity.mHandler.obtainMessage(MSG_TTS_END, tts).sendToTarget();
		}
	}
	@Override
	public void onTTSPlayCanceled(String tts) throws RemoteException {
		final VideoActivity2 videoActivity = (VideoActivity2)mActivityReference.get();
		if (videoActivity != null) {
		     videoActivity.mHandler.obtainMessage(MSG_TTS_CANCEL, tts).sendToTarget();
		}
	}
    }

    private void onTTSPlayEnd(String tts) {
    	synchronized (mLock) {
    	    Log.d(TAG, "onTTSPlayEnd "+tts+" "+mTTS+" "+getString(R.string.tts_video_record_start)+" "+mNeedStartPreview);
    	    if (mTTS != null && mTTS.equals(tts) && mTTS.equals(getString(R.string.tts_video_record_start))) {
		mTTS = null;
		if (mNeedStartPreview) {
		    startVideoRecording();	
		}
	    } else {
    		mTTS = null;
    	    }
    	}
    }

    private void onTTSPlayCanceled(String tts) {
	synchronized (mLock) {
	    Log.d(TAG, "onTTSPlayCanceled "+tts+" "+mTTS+" "+mNeedStartPreview);
	    if (mTTS != null && mTTS.equals(tts) && mTTS.equals(getString(R.string.tts_video_record_start))) {
		mTTS = null;
		if (mNeedStartPreview) {
		    startVideoRecording();	
		}
	    } else {
    		mTTS = null;
    	    }
	}
    }

    private void checkVideoFileSize () {
	    long storageSpace = StorageSpaceUtil.getAvailableSpace();
	    long estimateVideoFileSize =
		    mSurfaceWidth * mSurfaceHeight * 3 / 2 / 10 // A frame size
		    * 20 // 1 second
		    * mSubsectionTimed*60; // A video file subsection time.
	    if (CameraAppImpl.DEBUG) Log.d(TAG, "storageSpace=" + storageSpace);
	    if (storageSpace < estimateVideoFileSize) {
		    if (storageSpace <= 0) {
			    recyclingVideoSpace();
		    }
	    }
    }

    private void recyclingVideoSpace () {
	  // In car mode, if there is no enough storage space, then delete the earliest video files.
	// if (CAR_MODE_Y == mCarMode) {
	//     File videoFile = null;
	//     while (null != (videoFile = mVideoFileQueue.poll())) {
	// 	if (videoFile.exists() && videoFile.delete()) {
	// 	    if (CameraAppImpl.DEBUG) Log.d(TAG, "WARNING: Car Mode, No enough storage space! "
	// 			     + "Delete file: " + videoFile.getAbsolutePath() + " success!");
	// 	    break;
	// 	}
	// 	if (CameraAppImpl.DEBUG) Log.d(TAG, "WARNING: Car Mode, No enough storage space! "
	// 			 + "Delete file: " + videoFile.getAbsolutePath() + " failure!");
	//     }
	        	
	//     if(videoFile == null) {
	// 	mErrorType = ERROR_TYPE_NOSPACE;
	// 	if(mMediaRecorderRecording) {
	// 	    finish();
	// 	}
	// 	else {
	// 	    mCameraDevice.lock();
	// 	    releaseMediaRecorder();
	// 	    return;
	// 	}
	//     }
	// } else {
	    mErrorType = ERROR_TYPE_NOSPACE;
	    if(mMediaRecorderRecording) {
		releaseResource();
		finish();
	    }
	    else {
		mCameraDevice.lock();
		releaseMediaRecorder();
		return;
	    }
	// }
    }

	/*
	 *create video thumbnail and save it to /apache/GlassData/.videothumbnail
	 *by hky@sctek.cn 20150804
	 */
	public void creatAndSaveVideoThumbnail(String path) {
	    final String videoPath = path;
	    new Thread(new Runnable() {
		    @Override
			public void run() {
			Log.i(TAG,"creatAndSaveVideoThumbnail thread id="+Thread.currentThread().getId());
			try {
			
			    Bitmap bitmap = Thumbnail.createVideoThumbnail(videoPath, 512);
			
			    if(bitmap == null)
				return;
			
			    String savePath = videoPath.replace("videos", ".videothumbnails");

			    File thumbfile = new File(savePath);
			    File videothumbnails = new File(thumbfile.getParent());
			    if (!videothumbnails.exists()) {
				videothumbnails.mkdirs();
				try {
				    Runtime.getRuntime().exec("chmod 777 " + videothumbnails.getAbsolutePath());
				} catch (Exception e) {
				    e.printStackTrace();
				}
			    }

			    thumbfile.delete();
			    thumbfile.createNewFile();
			    thumbfile.setReadable(true, false);
			    thumbfile.setWritable(true, false);
			    thumbfile.setExecutable(true, false);
			
			    FileOutputStream fos = new FileOutputStream(savePath);
			    bitmap.compress(CompressFormat.JPEG, 100, fos);
			    fos.flush();
			    fos.close();
		
			} catch (Exception e) {
			    e.printStackTrace();
			}

		    }
		}).start();
	}

    private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
	    public void onAudioFocusChange(int focusChange) {
	    }
	};

    private void setProfile() {
 	mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setVideoFrameRate(24);
        mMediaRecorder.setVideoSize(mDesiredPreviewWidth,mDesiredPreviewHeight);
        mMediaRecorder.setVideoEncodingBitRate(6000000);

        if (mProfile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW &&
	    mProfile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) {
	      // Nothing needs to be done. Call to setCaptureRate() enables
	      // time lapse video recording.
        } else {
	    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
	    mMediaRecorder.setAudioEncodingBitRate(13200);//aac adts when <=12200, latm when > 12200, now we use latm
	    mMediaRecorder.setAudioChannels(1);
	    mMediaRecorder.setAudioSamplingRate(16000);
        }
    }

    
    public void takePicture() {
	Log.d(TAG,"mMediaRecorderRecording="+mMediaRecorderRecording);
        if (mCameraDevice == null || !mMediaRecorderRecording) {
            return ;
        }
        mCameraDevice.takePicture(null, null, null, this);
    }
    
    @Override
    public void onPictureTaken(final byte [] jpegData, final android.hardware.Camera camera) {
	Util.storeImage(this,jpegData,mParameters.getPictureSize());
    }


}
 